package com.hwtx.form.domain.handle;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Joiner;
import com.hwtx.form.annotation.FormId;
import com.hwtx.form.annotation.FormType;
import com.hwtx.form.domain.def.FormDef;
import com.hwtx.form.domain.def.meta.config.FormComponentConfig;
import com.hwtx.form.domain.handle.datamodel.DataModelDetail;
import com.hwtx.form.domain.listener.DataPermValueChangeEvent;
import com.hwtx.form.domain.listener.EventPublisher;
import com.hwtx.form.domain.repo.FormRepo;
import com.hwtx.form.domain.service.ResourceService;
import com.hwtx.form.domain.vo.FormData;
import com.hwtx.form.domain.vo.FormRepData;
import com.hwtx.form.domain.vo.ResultVo;
import com.hwtx.form.persistence.entity.SysRoleDataPermPro;
import com.hwtx.form.persistence.mapper.SysRoleDataPermProMapper;
import com.hwtx.form.query.FormValueQuery;
import com.hwtx.form.util.BeanUtil;
import com.hwtx.form.util.FormUtil;
import com.hwtx.form.util.JacksonUtil;
import io.geekidea.boot.framework.exception.BusinessException;
import io.geekidea.boot.system.entity.SysMenu;
import io.geekidea.boot.system.enums.SysMenuType;
import io.geekidea.boot.system.vo.DataPermissionConfig;
import io.geekidea.boot.system.vo.SysMenuVo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hwtx.form.domain.FormConstants.*;
import static com.hwtx.form.util.JacksonUtil.map2Oject;

@FormType(value = RESOURCE_FORM_HANDLE_NAME, actionClass = ResourceFormHandleAction.class)
@FormId(USER_ROLE_TAB_FORM_ID)
@Slf4j
public class ResourceFormHandle implements FormDataHandle, FormDefHandle {

    @Resource
    ResourceService resourceService;
    @Resource
    EventPublisher eventPublisher;
    @Resource
    private FormRepo formRepo;
    @Resource
    private SysRoleDataPermProMapper sysRoleDataPermProMapper;

    @Override
    public FormRepData getFormData(FormDef formDef, FormValueQuery formValueQuery) {
        List<Long> valueIds = formValueQuery.getValueIds();
        SysMenuVo sysMenuVo = new SysMenuVo();
        if (CollectionUtils.isNotEmpty(valueIds)) {
            SysMenu sysMenu = resourceService.getMenuById(valueIds.getFirst());
            BeanUtil.copyFieldValue(sysMenuVo, sysMenu);
            sysMenuVo.setFfId(sysMenu.getFId());
            if (sysMenu.getParentId() == 0) {
                sysMenuVo.setParentType(SysMenuType.ROOT.getCode());
            } else {
                SysMenu parent = resourceService.getMenuById(sysMenu.getParentId());
                sysMenuVo.setParentType(parent.getType());
            }
            if (StringUtils.isNotEmpty(sysMenu.getDataPermConfig())) {
                List<DataPermissionConfig.RDataPermissionConfig> configs = JacksonUtil.json2Array(sysMenu.getDataPermConfig(), new TypeReference<>() {
                });
                if (CollectionUtils.isNotEmpty(configs)) {
                    sysMenuVo.setDataPermConfig(configs.stream().map(config -> {
                        DataPermissionConfig.SeriableDataPermissionConfig seriableDataPermissionConfig = new DataPermissionConfig.SeriableDataPermissionConfig();
                        seriableDataPermissionConfig.setModelName(config.getModelName());
                        seriableDataPermissionConfig.setName(config.getName());
                        seriableDataPermissionConfig.setType(config.getType());
                        seriableDataPermissionConfig.setController(config.getController());
                        seriableDataPermissionConfig.setRemark(config.getRemark());
                        seriableDataPermissionConfig.setApiCode(config.getApiCode());
                        if (config.getOutput() != null) {
                            seriableDataPermissionConfig.setOutput(config.getOutput().stream().map(output -> output.getKey() + ":" + output.getAlias()).collect(Collectors.joining(",")));
                        }
                        return seriableDataPermissionConfig;
                    }).toList());
                }
            }
        }
        Long fId = formValueQuery.getFId();
        if (fId != null) {
            try {
                FormDef formInfo = formRepo.getFormDef(fId);
                sysMenuVo.setName(FormI18nHandle.getValue(formInfo.getMeta().getTitle()));
                if (!Objects.equals(formInfo.getType(), formInfo.getName())) {
                    sysMenuVo.setRouteUrl("/" + formInfo.getType() + "/" + formInfo.getName());
                } else {
                    sysMenuVo.setRouteUrl("/" + formInfo.getName());
                }
                sysMenuVo.setCode(formInfo.getName());
                sysMenuVo.setIsShow(1);
                sysMenuVo.setType(SysMenuType.MENU.getCode());
                sysMenuVo.setClose(1);
                sysMenuVo.setStatus(true);
            } catch (Exception ignored) {
            }
        }
        return FormRepData.builder().data(BeanMap.create(sysMenuVo)).build();
    }

    @Override
    public Object saveFormData(FormDef formDef, FormData formData) {
        SysMenu sysMenu = map2Oject(formData.getData(), SysMenu.class);
//        FormDef targetFormDef = formRepo.getFormDefByName(sysMenu.getCode());
//        if (targetFormDef != null) {
//            if (targetFormDef.getMeta() == null || StringUtils.isBlank(targetFormDef.getMeta().getTitle()) ||
//                    StringUtils.isBlank(targetFormDef.getMeta().getGenBy())) {
//                return ResultVo.fail("请生成表单明细");
//            }
//        } F
        if (Objects.equals(sysMenu.getType(), SysMenuType.FUN_PERMISSION.getCode())) {
            if (StringUtils.isNotEmpty(sysMenu.getRouteUrl()) && !sysMenu.getRouteUrl().startsWith("/")) {
                return ResultVo.fail("请填写正确的资源路径");
            }
        }
        List<SysMenu> menus = null;

        if (StringUtils.isNotEmpty(sysMenu.getCode())) {
            SysMenu menu = new SysMenu();
            menu.setCode(sysMenu.getCode());
            menus = resourceService.getResources(menu);
        }
        String ffId = FormUtil.getStrPostValue(formData.getData(), "ffId");
        if (StringUtils.isNotBlank(ffId)) {
            sysMenu.setFId(Long.valueOf(ffId));
        }
        if (sysMenu.getId() == null) {
            if (CollectionUtils.isEmpty(menus)) {
                return resourceService.addSysMenu(sysMenu);
            } else {
                return ResultVo.fail("已存在相同资源标识");
            }
        } else {
            if (CollectionUtils.isEmpty(menus) || Objects.equals(menus.getFirst().getId(), sysMenu.getId())) {
                if (BooleanUtils.toBoolean(FormUtil.getStrPostValue(formData.getData(), "updatePerm"))) {
                    SysMenu dbMenu = resourceService.getMenuById(sysMenu.getId());
                    List<DataPermissionConfig.WDataPermissionConfig> newConfig = null;
                    if (StringUtils.isNotEmpty(sysMenu.getDataPermConfig())) {
                        newConfig = JacksonUtil.json2Array(sysMenu.getDataPermConfig(), new TypeReference<>() {
                        });
                    }
                    if (!Objects.equals(dbMenu.getDataPermConfig(), sysMenu.getDataPermConfig())) {
                        checkDataPermConfig(dbMenu.getDataPermConfig(), newConfig, sysMenu.getId());
                    }
                    if (newConfig != null) {
                        AtomicBoolean change = new AtomicBoolean(false);
                        newConfig.stream().filter(config -> StringUtils.isNotEmpty(config.getModelName())).forEach(config -> {
                            List<FormComponentConfig.ParamMapping> output = handleDataPemOutput(config.getOutput(), config.getModelName());
                            config.setOutput(output);
                            change.set(true);
                        });
                        if (change.get()) {
                            sysMenu.setDataPermConfig(JacksonUtil.object2json(newConfig));
                            eventPublisher.publishEvent(new DataPermValueChangeEvent(this, sysMenu.getId()));

                        }
                    }
                    sysMenu.setName(dbMenu.getName());
                    return resourceService.updateDataPermission(sysMenu);
                }
                return resourceService.updateSysMenu(sysMenu);
            } else {
                return ResultVo.fail("已存在相同资源编码");
            }
        }
    }

    private List<FormComponentConfig.ParamMapping> handleDataPemOutput(List<?> output, String modelName) {
        if (CollectionUtils.isNotEmpty(output)) {
            List<FormComponentConfig.ParamMapping> ret = new ArrayList<>();
            output.forEach(out -> {
                Map<String, String> item = (Map<String, String>) out;
                ret.addAll(item.entrySet().stream().map(entry -> {
                    FormComponentConfig.ParamMapping paramMapping = new FormComponentConfig.ParamMapping();
                    paramMapping.setKey(entry.getKey());
                    paramMapping.setAlias(entry.getValue());
                    return paramMapping;
                }).toList());
            });
            return ret;
        } else {
            List<String> outputParams = FormUtil.getDataModelDetails(modelName).stream().map(DataModelDetail::getName).toList();
            return outputParams.stream().map(out -> {
                FormComponentConfig.ParamMapping paramMapping = new FormComponentConfig.ParamMapping();
                paramMapping.setKey(out);
                return paramMapping;
            }).toList();
        }
    }

    private void checkDataPermConfig(String oldDataPermConfig, List<DataPermissionConfig.WDataPermissionConfig> newConfig, Long resourceId) {
        if (StringUtils.isEmpty(oldDataPermConfig)) {
            return;
        }
        List<DataPermissionConfig.WDataPermissionConfig> oldConfig = JacksonUtil.json2Array(oldDataPermConfig, new TypeReference<>() {
        });
        List<SysRoleDataPermPro> propertyValues = sysRoleDataPermProMapper.selectList(new LambdaQueryWrapper<SysRoleDataPermPro>().eq(SysRoleDataPermPro::getMenuId, resourceId));
        if (propertyValues.isEmpty()) {
            return;
        }
        if (CollectionUtils.isNotEmpty(oldConfig) && CollectionUtils.isEmpty(newConfig)) {
            Map<String, DataPermissionConfig> configMap = oldConfig.stream().collect(Collectors.toMap(this::getDataPermName, Function.identity()));
            propertyValues.forEach(sysRoleDataPermPro -> JacksonUtil.json2Map(sysRoleDataPermPro.getDataPermConfig()).keySet().forEach(key -> {
                if (configMap.containsKey(key)) {
                    throw new BusinessException("请先删除角色数据权限属性,属性名：" + key);
                }
            }));
        } else if (CollectionUtils.isNotEmpty(newConfig) && CollectionUtils.isNotEmpty(oldConfig)) {
            CollectionUtils.subtract(oldConfig, newConfig).forEach(config -> {
                String key = getDataPermName(config);
                propertyValues.forEach(sysRoleDataPermPro -> {
                    if (JacksonUtil.json2Map(sysRoleDataPermPro.getDataPermConfig()).containsKey(key)) {
                        throw new BusinessException("请先删除角色数据权限属性,属性名：" + key);
                    }
                });
            });
        }
    }

    @Override
    public Object removeFormData(FormDef formDef, FormValueQuery formValueQuery) {
        if (CollectionUtils.isNotEmpty(formValueQuery.getValueIds())) {
            return resourceService.deleteSysMenu(formValueQuery.getValueIds().getFirst());
        }
        return false;
    }

    @Override
    public void handleDefContent(FormDef formDef, JsonNode rootNode, Map<String, String[]> formDefQuery) {
        String[] roles = formDefQuery.get("roles");
        if (ArrayUtils.isEmpty(roles)) {
            return;
        }
        ObjectNode defaultTab = (ObjectNode) rootNode.findParent("id");
        if (defaultTab == null) {
            log.error("查询资源树模板中缺少id属性");
            return;
        }
        ArrayNode tabs = (ArrayNode) rootNode.findPath("tabs");
        for (int i = 0; i < roles.length; i++) {
            String[] roleInfo = getRoleId(roles[i]);
            if (ArrayUtils.isEmpty(roles)) {
                continue;
            }
            assert roleInfo != null;
            String roleName = roleInfo[0];
            String roleId = roleInfo[1];
            ObjectNode tabToProcess;
            if (i == 0) {
                tabToProcess = defaultTab;
            } else {
                tabToProcess = defaultTab.deepCopy();
                tabs.add(tabToProcess);
            }
            tabToProcess.put("title", roleName);
            ObjectNode body = (ObjectNode) tabToProcess.findPath("body");
            body.put("name", RESOURCE_NAME_PREFIX + roleId);
            ObjectNode roleIdObject = tabToProcess.findParent("roleId");
            roleIdObject.put("roleId", roleId);
            tabToProcess.put("id", "tab-" + roleId);
        }
    }

    private String[] getRoleId(String role) {
        if (StringUtils.isNotBlank(role)) {
            String[] roleInfo = role.split(":");
            if (roleInfo.length != 2) {
                return null;
            }
            return roleInfo;
        }
        return null;
    }

    private String getDataPermName(DataPermissionConfig config) {
        String name = config.getModelName();
        if (StringUtils.isNotEmpty(name)) {
            return name;
        }
        if (StringUtils.isNotEmpty(config.getName())) {
            name = config.getName();
        }
        String[] names = name.split(",");
        if (names.length > 1) {
            return Joiner.on("_").join(names);
        }
        return name;
    }
}
